package cn.sylinx.hbatis.ext.lambda;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import cn.sylinx.hbatis.exception.HbatisException;

public class LambdaUtil {

	private static final Map<SerializedLambda, String> CACHE_FIELD_NAME = new ConcurrentHashMap<>(8);

	public static String methodToFieldName(String methodName) {
		return capitalize(methodName.replace("get", ""));
	}

	public static String capitalize(String input) {
		return input.substring(0, 1).toLowerCase() + input.substring(1, input.length());
	}

	private static String getLambdaFieldNameInner(SerializedLambda serializedLambda) {
		String name = CACHE_FIELD_NAME.get(serializedLambda);
		if (null != name) {
			return name;
		}
		String methodName = serializedLambda.getImplMethodName();
		String fieldName = methodToFieldName(methodName);
		CACHE_FIELD_NAME.put(serializedLambda, fieldName);
		return fieldName;
	}

	public static String getLambdaFieldName(Serializable lambda) {
		SerializedLambda serializedLambda = computeSerializedLambda(lambda);
		return getLambdaFieldNameInner(serializedLambda);
	}

	private static SerializedLambda computeSerializedLambda(Serializable lambda) {

		Class<?> cl = lambda.getClass();
		try {
			Method m = cl.getDeclaredMethod("writeReplace");
			m.setAccessible(true);
			Object replacement = m.invoke(lambda);
			if (replacement instanceof SerializedLambda) {
				return (SerializedLambda) replacement;
			}
		} catch (Exception e) {
			throw new HbatisException("get lambda column name fail", e);
		}
		return null;
	}
}
